#include <fuse.h>       
#include <stdio.h>      
#include <string.h>     
#include <unistd.h>     
#include <fcntl.h>      
#include <dirent.h>     
#include <errno.h>      
#include <sys/time.h>

#include <iostream>
#include <string>

#include <log4cxx/logger.h>
#include <log4cxx/xml/domconfigurator.h>

#include "SFSClient.h"

using std::string;
using std::cout;
using std::endl;

using namespace log4cxx;

static SFSClient g_sfs;
static string g_log_conf_file;
static string g_cs_ip;
static int g_cs_port;
static LoggerPtr g_logger;
static struct fuse_operations g_sfs_oper;

int parse_args(int argc, char * argv[]) {
	char c;
	while(-1 != (c = getopt(argc, argv, "l:"))) {
		switch(c) {
			case 'l':
				g_log_conf_file = optarg;
				break;

			default:
				break;
		}
	}
	return 1;
}



static int sfs_getattr(const char *path, struct stat *stbuf) {
	int ret = g_sfs.get_file_stat(path, *stbuf);
	if (ret < 0) return -ENOENT;
	return 0;
}

static int sfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi) {
	list<string> entries;
	int ret = g_sfs.read_dir(path, entries);
	if (ret < 0) return -ENOTDIR;
	for (list<string>::const_iterator iter = entries.begin(); entries.end() != iter; iter++) {
                filler(buf, iter->c_str(), NULL, 0);
	}
        return 0;
}

static int sfs_mknod(const char *path, mode_t mode, dev_t rdev) {
        if (!S_ISREG(mode)) return -EACCES;
	int ret = g_sfs.create_file(path);
	if (ret < 0) return -EACCES;
        return 0;
}

static int sfs_mkdir(const char *path, mode_t mode) {
	int ret = g_sfs.create_dir(path);
	if (ret < 0) return -EACCES;
        return 0;
}

static int sfs_unlink(const char *path) {
	int ret = g_sfs.delete_file(path);
	if (ret < 0) return -EACCES;
	return 0;
}

static int sfs_rmdir(const char *path) {
	int ret = g_sfs.delete_dir(path);
	if (ret < 0) return -ENOTEMPTY;
	return 0;
}

static int sfs_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
	int ret = g_sfs.read_file(path, buf, offset, size);
	if (ret < 0) return -EIO;
	return ret;
}

static int sfs_write(const char *path, const char *buf, size_t size, off_t offset, struct fuse_file_info *fi) {
	int write_mode = DS_WRITE_MODE_NORMAL;
	if (O_APPEND == (fi->flags & O_APPEND)) write_mode = DS_WRITE_MODE_APPEND;
	int ret = g_sfs.write_file(path, buf, size, offset, write_mode);
	if (ret < 0) return -EIO;
	return size;
}

static int sfs_utimens(const char *path, const struct timespec ts[2])
{
        return 0;
}

static int sfs_truncate(const char *path, off_t size)
{
        int ret = g_sfs.truncate(path, size);
        if (ret < 0) return -EACCES;
        return 0;
}

int init(int argc, char * argv[]) {
//	parse_args(argc, argv);
	log4cxx::xml::DOMConfigurator::configure(g_log_conf_file.c_str());
	g_logger = Logger::getLogger("sfs_fuse");
	int ret = g_sfs.init(g_log_conf_file.c_str(), g_cs_ip.c_str(), g_cs_port,  30, 10, 10, 10, -1);
        if (ret < 0) { cout << "sfs client init fail" << endl; return -1; }
	g_sfs_oper.getattr = sfs_getattr;
	g_sfs_oper.readdir = sfs_readdir;
        g_sfs_oper.mknod  = sfs_mknod;
        g_sfs_oper.mkdir  = sfs_mkdir;
        g_sfs_oper.unlink = sfs_unlink;
        g_sfs_oper.rmdir = sfs_rmdir;
        g_sfs_oper.read = sfs_read;
        g_sfs_oper.write = sfs_write;
        g_sfs_oper.utimens= sfs_utimens;
        g_sfs_oper.truncate= sfs_truncate;
	return 1;
}

int main(int argc, char * argv[]) {
	struct fuse_args args = FUSE_ARGS_INIT(0, NULL);
	for(int i = 0; i < argc; i++) {
		if (1 == i)
			g_log_conf_file = argv[i];
		else if (2 == i)
			g_cs_ip = argv[i];
		else if (3 == i)
			g_cs_port = atoi(argv[i]);
		else
			fuse_opt_add_arg(&args, argv[i]);
	}
	int ret = init(argc, argv);
	if (ret < 0) {
		cout << "init error, ret:" << ret << endl;
		return -1;
	}
	return fuse_main(args.argc, args.argv, &g_sfs_oper, NULL);
}
